package com.open.capacity.common.util.skf;

import com.sun.jna.Pointer;
import com.sun.jna.ptr.ByteByReference;
import com.sun.jna.ptr.LongByReference;
import com.sun.jna.ptr.PointerByReference;

public class SKF_TOOL {

    private static String userid = "1234567812345678";
    private Pointer p_hdev = null;
    private Pointer p_happ = null;
    private Pointer p_hcont = null;
    private Pointer p_phHash = null;

    private CLibrary ukey = CLibrary.INSTANCE;
    private String USER_PIN = "111111";

    public  SKF_TOOL(String user_pin) {
        USER_PIN = user_pin;
        int ret = -1 ;
        int alg_hash = 1;
        int APP_NAME_SIZE = 256;

        Util.blue("============SKF_TOOL init...==================");
        try{
            LongByReference lbr = new LongByReference();
            ret = ukey.SKF_EnumDev(true,null,lbr);
            long lv = lbr.getPointer().getLong(0);
            Util.blue(ret + " SKF_EnumDev 设备编号长度= [" + lv + "]");
            if(lv == 0){
                System.out.println("未插入USBKey！");
                return ;
            }

            ByteByReference bbr = new ByteByReference();
            LongByReference lbr_input = new LongByReference();
            lbr_input.setValue(lv);
            ret = ukey.SKF_EnumDev(true, bbr, lbr_input);
            String str_dev = bbr.getPointer().getString(0);
            Util.blue(ret + " SKF_EnumDev 设备编号= [" + str_dev + "]");

            PointerByReference lbr_hdev = new PointerByReference();
            ret = ukey.SKF_ConnectDev(str_dev, lbr_hdev);
            p_hdev = lbr_hdev.getValue();
            Util.blue(ret + " SKF_ConnectDev 设备hdev= [" + p_hdev + "]");

            ByteByReference bbr_applist = new ByteByReference();
            LongByReference lbr_pulSize = new LongByReference();
            lbr_pulSize.setValue(APP_NAME_SIZE);
            ret = ukey.SKF_EnumApplication(p_hdev, bbr_applist, lbr_pulSize);
            // Fuck!: bbr_applist最后有个空格！需要去掉！
            String app_name = bbr_applist.getPointer().getString(0).trim();
            Util.blue(ret + " SKF_EnumApplication app name= [" + app_name + "]");

            PointerByReference pbr_happ = new PointerByReference();
            ret = ukey.SKF_OpenApplication(p_hdev, app_name, pbr_happ);
            p_happ = pbr_happ.getValue();
            Util.blue(ret + " SKF_OpenApplication app handle = [" + p_happ + "]");

            LongByReference lbr_pulRetryCount = new LongByReference();
            ret = ukey.SKF_VerifyPIN(p_happ, 1, USER_PIN,lbr_pulRetryCount);
            Util.blue(ret + " SKF_VerifyPIN 重试次数 = [" + lbr_pulRetryCount.getValue() + "]");
            if(ret != 0){
                Util.red("SKF_VerifyPIN fail!");
                //throw new Exception("SKF_VerifyPIN fail!");
                System.out.println("SKF_VerifyPIN fail!");
            }
        }catch (Exception exp){
            exp.printStackTrace();
        }
        Util.blue("p_hdev =" + p_hdev);
        Util.blue("p_happ =" + p_happ);
        Util.blue("============SKF_TOOL init end.==================\r\n");
        return ;
    }
    public void close() {
        int ret = -1;
        Util.blue("\r\n============SKF_TOOL close...==================");
        try{
            if(p_phHash != null) {
                ret = ukey.SKF_CloseHandle(p_phHash);
                Util.blue("SKF_CloseHandle " + ret);
            }
            if(p_hcont != null){
                ret = ukey.SKF_CloseContainer(p_hcont);
                Util.blue("SKF_CloseContainer " + ret );
            }
            if(p_happ != null){
                ret = ukey.SKF_CloseApplication(p_happ);
                Util.blue("SKF_CloseApplication "+ ret );
            }
            if(p_hdev != null){
                ret = ukey.SKF_DisConnectDev(p_hdev);
                Util.blue("SKF_DisConnectDev "+ ret );
            }
        }catch (Exception exp){
            exp.printStackTrace();
        }
        Util.blue("============SKF_TOOL close end.==================");
        return  ;
    }

    public String sm3_no_userid(String text_plain) {
        return sm3_no_userid( text_plain,"utf-8");
    }
    public String sm3_no_userid(String text_plain,String charsetName) {
        int ret;
        String sm3_b64 = "";
        int alg_hash = 1;
        Util.blue("============SKF_TOOL SM3 Hash_no_userid ...==================");
        try {
            PointerByReference pbr_phHash = new PointerByReference();
            ret = ukey.SKF_DigestInit(p_hdev, alg_hash, null,null,0,pbr_phHash);
            p_phHash = pbr_phHash.getValue();
            Util.red(ret + " SKF_DigestInit Handle_hash = [" + p_phHash + "]");

            LongByReference lbr_pulHashLen = new LongByReference();
            ret = ukey.SKF_Digest(p_phHash, text_plain.getBytes(charsetName), text_plain.length(), null, lbr_pulHashLen);
            Util.red(ret + " SKF_Digest bbr_pbHashData size = [" + lbr_pulHashLen.getValue() + " bits ]");

            Struct_SM3BLOB pbr_pbHashData = new Struct_SM3BLOB();
            //lbr_pulHashLen.setValue(32);
            ret = ukey.SKF_Digest(p_phHash, text_plain.getBytes(charsetName), text_plain.length(), pbr_pbHashData, lbr_pulHashLen);
            sm3_b64 = pbr_pbHashData.get_sm3_b64();
            Util.red(ret + " SKF_Digest bbr_pbHashData SM3 Base64 = [" + sm3_b64 + "]");
        } catch (Exception exp) {
            exp.printStackTrace();
        }
        Util.blue("============SKF_TOOL SM3 Hash_no_userid end.==================\r\n");
        return sm3_b64;
    }

    public String sm3_with_userid(String text_plain) {
        return sm3_with_userid(text_plain,"utf-8");
    }
    public String sm3_with_userid(String text_plain, String charsetName) {
        int ret;
        String sm3_b64 = "";
        int alg_hash = 1;
        Util.blue("============SKF_TOOL SM3 Hash_with_userid ...==================");
        try {
            Struct_SM3BLOB pbr_pbHashData = sm3_with_userid_ex(text_plain, charsetName);

            LongByReference lbr_pulHashLen = new LongByReference();
            lbr_pulHashLen.setValue(256);

            ret = ukey.SKF_Digest(p_phHash, text_plain.getBytes(charsetName), text_plain.length(), pbr_pbHashData, lbr_pulHashLen);
            sm3_b64 = pbr_pbHashData.get_sm3_b64();
            Util.red(ret + " SKF_Digest bbr_pbHashData SM3 Base64 = [" + sm3_b64 + "]");
            return sm3_b64;
        } catch (Exception exp) {
            exp.printStackTrace();
        }
        Util.blue("============SKF_TOOL SM3 Hash_with_userid end.==================\r\n");
        return sm3_b64;
    }

    public Struct_SM3BLOB sm3_with_userid_ex(String text_plain, String charsetName) {
        int ret;
        String sm3_b64 = "";
        int alg_hash = 1;
        Struct_SM3BLOB pbr_pbHashData = null;

        Util.blue("============SKF_TOOL SM3 Hash_with_userid ...==================");
        try {
            LongByReference lbr_pulSize = new LongByReference();
            ret = ukey.SKF_EnumContainer(p_happ, null, lbr_pulSize);
            long pulSize = lbr_pulSize.getValue();
            Util.blue(ret + " SKF_EnumContainer 容器名大小 = [" + pulSize + "]");

            Struct_CONTAINER containers = new Struct_CONTAINER();
            ret = ukey.SKF_EnumContainer(p_happ, containers, lbr_pulSize);
            String cont_name = containers.getPointer().getString(0);
            Util.blue(ret + " SKF_EnumContainer 容器名 = [" + cont_name + "]");

            PointerByReference pbr_phContainer = new PointerByReference();
            ret = ukey.SKF_OpenContainer(p_happ, cont_name, pbr_phContainer);
            p_hcont = pbr_phContainer.getValue();
            Util.blue(ret + " SKF_OpenContainer 容器p_hcont = [" + p_hcont + "]");

            LongByReference lbr_pulContainerType = new LongByReference();
            ret = ukey.SKF_GetContainerType(p_hcont, lbr_pulContainerType);
            long pulContainerType = lbr_pulContainerType.getValue();
            Util.blue(ret + " SKF_GetContainerType 容器类型 (0表示未定、尚未分配类型或者为空容器，为1表示为RSA容器，为2表示为ECC容器。)= [" + pulContainerType + "]");
            if(pulContainerType == 2){
                alg_hash = 2;
            }

            LongByReference lbr_pulBlobLen = new LongByReference();
            ret = ukey.SKF_ExportPublicKey(p_hcont, false, null, lbr_pulBlobLen);
            long l_pulBlobLen = lbr_pulBlobLen.getValue();
            Util.red(ret + " SKF_ExportPublicKey 公钥大小 = [" + l_pulBlobLen + "]");

            Struct_ECCPUBLICKEYBLOB pubkey = new Struct_ECCPUBLICKEYBLOB();
            lbr_pulBlobLen.setValue(l_pulBlobLen);
            ret = ukey.SKF_ExportPublicKey(p_hcont, false, pubkey, lbr_pulBlobLen);
            Util.red(ret + " SKF_ExportPublicKey 公钥 bitlen= " + pubkey.get_BitLen());
            Util.red(ret + " SKF_ExportPublicKey 公钥x = [" + Util.bytesToHex(pubkey.XCoordinate) + "]");
            Util.red(ret + " SKF_ExportPublicKey 公钥y = [" + Util.bytesToHex(pubkey.YCoordinate) + "]");

            PointerByReference pbr_phHash = new PointerByReference();
            ret = ukey.SKF_DigestInit(p_hdev, alg_hash, pubkey, userid, userid.length(), pbr_phHash);
            p_phHash = pbr_phHash.getValue();
            Util.red(ret + " SKF_DigestInit Handle_hash = [" + p_phHash + "]");

            LongByReference lbr_pulHashLen = new LongByReference();
            ret = ukey.SKF_Digest(p_phHash, text_plain.getBytes(charsetName), text_plain.length(), null, lbr_pulHashLen);
            Util.red(ret + " SKF_Digest bbr_pbHashData size = [" + lbr_pulHashLen.getValue() + " bits ]");

            pbr_pbHashData = new Struct_SM3BLOB();
            ret = ukey.SKF_Digest(p_phHash, text_plain.getBytes(charsetName), text_plain.length(), pbr_pbHashData, lbr_pulHashLen);
            sm3_b64 = pbr_pbHashData.get_sm3_b64();
            Util.red(ret + " SKF_Digest bbr_pbHashData SM3 Base64 = [" + sm3_b64 + "]");
        } catch (Exception exp) {
            exp.printStackTrace();
        }
        Util.blue("============SKF_TOOL SM3 Hash_with_userid end.==================\r\n");
        return pbr_pbHashData;
    }

    public  Struct_ECCPUBLICKEYBLOB get_pubkey() {
        int ret ;
        String cert_p7 = "";
        Struct_ECCPUBLICKEYBLOB pubkey = null;
        Util.blue("============SKF_TOOL getPubkey ...==================");
        try{
            LongByReference lbr_pulBlobLen = new LongByReference();
            ret = ukey.SKF_ExportPublicKey(p_hcont, false, null, lbr_pulBlobLen);
            long l_pulBlobLen = lbr_pulBlobLen.getValue();
            Util.red(ret + " SKF_ExportPublicKey 公钥大小 = [" + l_pulBlobLen + "]");

            //PointerByReference xxx[] = new PointerByReference[128];
            //PointerByReference xxx[] = new PointerByReference[(int)l_pulBlobLen];
            pubkey = new Struct_ECCPUBLICKEYBLOB();
            lbr_pulBlobLen.setValue(l_pulBlobLen);
            ret = ukey.SKF_ExportPublicKey(p_hcont, false, pubkey, lbr_pulBlobLen);
            Util.red(ret + " SKF_ExportPublicKey 公钥 bitlen= " + pubkey.get_BitLen());
            Util.red(ret + " SKF_ExportPublicKey 公钥x = [" + Util.bytesToHex(pubkey.XCoordinate) + "]");
            Util.red(ret + " SKF_ExportPublicKey 公钥y = [" + Util.bytesToHex(pubkey.YCoordinate) + "]");
        }catch (Exception exp){
            exp.printStackTrace();
        }
        Util.blue("============SKF_TOOL getPubkey end.==================");
        return pubkey;
    }
    public  String get_cert() {
        int ret ;
        String cert_p7 = "";

        try{
            LongByReference lbr_pulSize = new LongByReference();
            ret = ukey.SKF_EnumContainer(p_happ, null,lbr_pulSize);
            long pulSize = lbr_pulSize.getValue();
            Util.blue(ret + " SKF_EnumContainer 容器名大小 = [" + pulSize + "]");

            Struct_CONTAINER containers= new Struct_CONTAINER();
            ret = ukey.SKF_EnumContainer(p_happ, containers,lbr_pulSize);
            String cont_name = containers.getPointer().getString(0);
            Util.blue(ret + " SKF_EnumContainer 容器名 = [" + cont_name + "]");

            PointerByReference pbr_phContainer = new PointerByReference();
            ret = ukey.SKF_OpenContainer(p_happ, cont_name,pbr_phContainer);
            p_hcont = pbr_phContainer.getValue();
            Util.blue(ret + " SKF_OpenContainer 容器p_hcont = [" + p_hcont + "]");

            LongByReference lbr_pulCertLen = new LongByReference();
            ret = ukey.SKF_ExportCertificate(p_hcont, false, null, lbr_pulCertLen);
            int l_pulCertLen = (int)lbr_pulCertLen.getValue();
            Util.red(ret + " SKF_ExportCertificate 证书长度xx = [" + l_pulCertLen + "]");

            Struct_ECCCERTBLOB pbr_pbCert = new Struct_ECCCERTBLOB.ByReference();
            // TRUE表示签名证书，FALSE表示加密证书
            ret = ukey.SKF_ExportCertificate(p_hcont, false, pbr_pbCert,lbr_pulCertLen);
            cert_p7 = pbr_pbCert.get_cert();
            Util.blue(ret + " SKF_ExportCertificate 证书? = [" + cert_p7 + "]");

        }catch (Exception exp){
            exp.printStackTrace();
        }

        return cert_p7;
    }


    public String sign_p1( String text_plain,String USER_PIN) {
        return sign_p1( text_plain,"utf-8",USER_PIN);
    }
    public String sign_p1( String text_plain,String charsetName,String USER_PIN) {
        int ret ;
        String sign_p1 = "";
        int alg_hash = 1;
        Util.blue("============SKF_TOOL Sign P1...==================");

        try{
            Struct_SM3BLOB pbr_pbHashData =sm3_with_userid_ex(text_plain, "utf-8");

            Struct_ECCSIGNATUREBLOB pbr_sign = new Struct_ECCSIGNATUREBLOB();
            ret = ukey.SKF_ECCSignData(p_hcont,pbr_pbHashData,32 ,pbr_sign);
            //Util.blue(ret + " SKF_ECCSignData p1签名长度 = [" + lbr_pulSigLen.getValue() + "]");
            sign_p1 = pbr_sign.get_sign();
            Util.blue(ret + " SKF_ECCSignData p1签名 = [" + sign_p1 + "]");
        }catch (Exception exp){
            exp.printStackTrace();
        }
        Util.blue("============SKF_TOOL Sign P1 end.==================\r\n");
        return sign_p1;
    }

    public  int verify_p1( String text_plain,String sign_p1) {
        return verify_p1(text_plain,"utf-8", sign_p1);
    }
    public  int verify_p1( String text_plain,String charsetName,String sign_p1) {
        int ret = -1;
        int ret_verify = -1;
        //String sign_p1 = "";
        int alg_hash = 1;
        Util.blue("============SKF_TOOL Verify P1 ...==================");

        try{
            Struct_SM3BLOB pbr_pbHashData =sm3_with_userid_ex(text_plain, "utf-8");

            Struct_ECCSIGNATUREBLOB pbr_sign = new Struct_ECCSIGNATUREBLOB();
            pbr_sign.parse_from_p1(sign_p1);

            Struct_ECCPUBLICKEYBLOB pubkey = get_pubkey();

            //SKF_ECCVerify(Pointer hDev, Struct_ECCPUBLICKEYBLOB pbData, int ulDataLen, Struct_ECCSIGNATUREBLOB pSignature);
            ret = ukey.SKF_ECCVerify(p_hdev, pubkey,  pbr_pbHashData,32,pbr_sign);
            Util.blue(ret + " SKF_ECCVerify 验证签名 = [" + ret + "]");
            ret_verify = ret;
        }catch (Exception exp){
            exp.printStackTrace();
        }
        Util.blue("============SKF_TOOL Verify P1 end.==================");
        return ret_verify;
    }

}
